#include <stdio.h>
#include <stdlib.h>
#include <fstream>
#include <string.h>
#include <stdarg.h>
#include <windows.h>
#include "assert.h"
#include "util/time.h"
#include "system/criticalsection.h"

namespace gecko
{

#if defined(_USE_ASSERTS)

static inline bool CanDebugBreak()
{
	return IsDebuggerPresent() == TRUE;
}

static inline void DebugBreak()
{
	__debugbreak();
}

static char LOGFILE[32] = "debug.txt";
static const int  MAX_TEXT_LEN = 2048;
char LogFilterString[MAX_TEXT_LEN] = {0};

void SetLogFile(const char* filename)
{
	strcpy(LOGFILE, filename);
}

void Log( const char* msg)
{
	static CriticalSection cs;
	cs.Begin();	// lock critical section to prevent multiple threads from logging at the same time.

	// check if the message should be filtered out
	bool outputMessage = strstr(msg, LogFilterString) != NULL;

	if (outputMessage)
	{
		char bigText[MAX_TEXT_LEN];
	
		SystemTime localTime;
		GetLocalTime(&localTime);
	
		sprintf(bigText, "%02d:%02d:%02d.%03d - ", localTime.hour, localTime.min, localTime.sec, localTime.msec);
	
		strcat(bigText, msg);
		strcat(bigText, "\n");
	
		// TODO use FileStream here instead
		std::fstream file(LOGFILE, std::ios_base::out | std::ios_base::app);
		file << bigText;
		file.close();
	
		OutputDebugStringA(bigText);
	}

	cs.End(); // unlock the resource.
}

void ClearLog()
{
	std::fstream file(LOGFILE, std::ios_base::out);
	file.clear();
	file.close();
}

void SetDebugLogFilter(const char * filterString)
{
	strncpy(LogFilterString, filterString, MAX_TEXT_LEN);
}

namespace _internal
{

void _debugPrint(const char* errorMsg, ...)
{
	char bigErrorText[MAX_TEXT_LEN];

	char formattedText[MAX_TEXT_LEN];
	va_list vl;
	va_start(vl, errorMsg);
	vsnprintf(formattedText, MAX_TEXT_LEN, errorMsg, vl);
	va_end(vl);

	sprintf(bigErrorText, "%s", formattedText);
	Log(bigErrorText);
}

void _warning(const char* file, int line, const char* errorMsg, ...)
{
	char bigErrorText[MAX_TEXT_LEN];

	char formattedText[MAX_TEXT_LEN];
	va_list vl;
	va_start(vl, errorMsg);
	vsnprintf(formattedText, MAX_TEXT_LEN, errorMsg, vl);
	va_end(vl);

	sprintf(bigErrorText, "WARNING: %s - (%s, line %d)", formattedText, file, line);
	Log(bigErrorText);
}

void _criticalError(const char* file, int line, const char* errorMsg, ...)
{
	char bigErrorText[MAX_TEXT_LEN];

	char formattedText[MAX_TEXT_LEN];
	va_list vl;
	va_start(vl, errorMsg);
	vsnprintf(formattedText, MAX_TEXT_LEN, errorMsg, vl);
	va_end(vl);

	sprintf(bigErrorText, "ERROR: %s - (%s, line %d)", formattedText, file, line);
	Log(bigErrorText);

	if ( CanDebugBreak() )
	{
		DebugBreak();
	}
	else
	{
		::MessageBoxA(NULL,  LPCSTR(bigErrorText),  "GeckoTech Engine", MB_ICONERROR | MB_OK);
		
		// enter a closed loop to wait for a debugger
		/*bool loop = true;
		while ( loop )
		{
			ThreadSleep(10);
			if ( CanDebugBreak() )
			{
				DebugBreak();
			}
		}*/
	}

	Log("Exiting...");
	exit(EXIT_FAILURE);
}

void _customAssert( const char* file, int line, const char* errorMsg, ... )
{
	char bigErrorText[MAX_TEXT_LEN];

	char formattedText[MAX_TEXT_LEN];
	va_list vl;
	va_start(vl, errorMsg);
	vsnprintf(formattedText, MAX_TEXT_LEN, errorMsg, vl);
	va_end(vl);

	sprintf(bigErrorText, "ASSERTION: %s - (%s, line %d)", formattedText, file, line);
	Log(bigErrorText);

	if ( CanDebugBreak() )
	{
		DebugBreak();
	}
	else
	{
		::MessageBox(NULL, bigErrorText, "GeckoTech Engine", MB_ICONERROR | MB_OK);
		Log("Exiting...");
		exit(EXIT_FAILURE);
	}	
}

} // namespace _internal

#endif // defined(_DEBUG) || defined(_ASSERT_IN_RELEASE)

} // namespace gecko

